// 版权所有2021 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// packagefiat使用经过正式验证的算法实现基本顺序字段
// 来自菲亚特加密项目。
package fiat

import (
	"crypto/subtle"
	"errors"
)

// P521元素是模2^521-1的整数。
// None
// 零值是有效的零元素。
type P521Element struct {
	// 此元素具有以下界限，这些界限比
	// 某些操作的输出边界。这些行动必须得到加强
	// 接着是一个携带。
	// None
	// [0x0~>0x400000000000000]，[0x0~>0x400000000000000]，[0x0~>0x400000000000000]，
	// [0x0~>0x400000000000000]，[0x0~>0x400000000000000]，[0x0~>0x400000000000000]，
	// [0x0~>0x400000000000000]，[0x0~>0x400000000000000]，[0x0~>0x2000000000000000]
	x [9]uint64
}

// 设置e=1，并返回e。
func (e *P521Element) One() *P521Element {
	*e = P521Element{}
	e.x[0] = 1
	return e
}

// 如果e==t，Equal返回1，否则返回0。
func (e *P521Element) Equal(t *P521Element) int {
	eBytes := e.Bytes()
	tBytes := t.Bytes()
	return subtle.ConstantTimeCompare(eBytes, tBytes)
}

var p521ZeroEncoding = new(P521Element).Bytes()

// 如果e==0，IsZero返回1，否则返回0。
func (e *P521Element) IsZero() int {
	eBytes := e.Bytes()
	return subtle.ConstantTimeCompare(eBytes, p521ZeroEncoding)
}

// Set设置e=t，并返回e。
func (e *P521Element) Set(t *P521Element) *P521Element {
	e.x = t.x
	return e
}

// Bytes返回e的66字节小端编码。
func (e *P521Element) Bytes() []byte {
	// 必须内联此函数才能将分配移动到父级和
	// 将其从转义到堆中保存。
	var out [66]byte
	p521ToBytes(&out, &e.x)
	return out[:]
}

// SetBytes设置e=v，其中v是一个小的endian 66字节编码，并返回
// E如果v不是66字节，或者它编码的值大于2^521-1，则为SetBytes
// 返回nil和一个错误，而e保持不变。
func (e *P521Element) SetBytes(v []byte) (*P521Element, error) {
	if len(v) != 66 || v[65] > 1 {
		return nil, errors.New("invalid P-521 field encoding")
	}
	var in [66]byte
	copy(in[:], v)
	p521FromBytes(&e.x, &in)
	return e, nil
}

// Add设置e=t1+t2，并返回e。
func (e *P521Element) Add(t1, t2 *P521Element) *P521Element {
	p521Add(&e.x, &t1.x, &t2.x)
	p521Carry(&e.x, &e.x)
	return e
}

// 子集e=t1-t2，并返回e。
func (e *P521Element) Sub(t1, t2 *P521Element) *P521Element {
	p521Sub(&e.x, &t1.x, &t2.x)
	p521Carry(&e.x, &e.x)
	return e
}

// Mul设置e=t1*t2，并返回e。
func (e *P521Element) Mul(t1, t2 *P521Element) *P521Element {
	p521CarryMul(&e.x, &t1.x, &t2.x)
	return e
}

// 平方集e=t*t，并返回e。
func (e *P521Element) Square(t *P521Element) *P521Element {
	p521CarrySquare(&e.x, &t.x)
	return e
}

// 如果cond==1，选择将e设置为a，如果cond==0，选择将e设置为b。
func (v *P521Element) Select(a, b *P521Element, cond int) *P521Element {
	p521Selectznz(&v.x, p521Uint1(cond), &b.x, &a.x)
	return v
}

// 反转设置e=1/t，并返回e。
// None
// 如果t==0，则Invert返回e=0。
func (e *P521Element) Invert(t *P521Element) *P521Element {
	// None
	// 乘法和平方的序列是用
	// github.com/mmcloughlin/addchain v0.2.0。

	var t1, t2 = new(P521Element), new(P521Element)

	// _10 = 2 * 1
	t1.Square(t)

	// _11 = 1 + _10
	t1.Mul(t, t1)

	// _1100 = _11 << 2
	t2.Square(t1)
	t2.Square(t2)

	// _1111 = _11 + _1100
	t1.Mul(t1, t2)

	// _11110000 = _1111 << 4
	t2.Square(t1)
	for i := 0; i < 3; i++ {
		t2.Square(t2)
	}

	// _11111111 = _1111 + _11110000
	t1.Mul(t1, t2)

	// x16=11111111<<8+11111111
	t2.Square(t1)
	for i := 0; i < 7; i++ {
		t2.Square(t2)
	}
	t1.Mul(t1, t2)

	// x32=x16<<16+x16
	t2.Square(t1)
	for i := 0; i < 15; i++ {
		t2.Square(t2)
	}
	t1.Mul(t1, t2)

	// x64=x32<<32+x32
	t2.Square(t1)
	for i := 0; i < 31; i++ {
		t2.Square(t2)
	}
	t1.Mul(t1, t2)

	// x65=2*x64+1
	t2.Square(t1)
	t2.Mul(t2, t)

	// x129=x65<<64+x64
	for i := 0; i < 64; i++ {
		t2.Square(t2)
	}
	t1.Mul(t1, t2)

	// x130=2*x129+1
	t2.Square(t1)
	t2.Mul(t2, t)

	// x259=x130<<129+x129
	for i := 0; i < 129; i++ {
		t2.Square(t2)
	}
	t1.Mul(t1, t2)

	// x260=2*x259+1
	t2.Square(t1)
	t2.Mul(t2, t)

	// x519=x260<<259+x259
	for i := 0; i < 259; i++ {
		t2.Square(t2)
	}
	t1.Mul(t1, t2)

	// 返回x519<<2+1
	t1.Square(t1)
	t1.Square(t1)
	return e.Mul(t1, t)
}
